/**
 * @author     Gabor Greif <ggreif@gmail.com>
 * @copyright  2009-2010 Gabor Greif
 * @license    http://www.opensource.org/licenses/mit-license.php  MIT License
 * @version    Release: 0.0
 */

#ifndef micker_ARITIES_hpp
#define micker_ARITIES_hpp

#include "Env.hpp"

#include <algorithm>
#include <deque>

namespace micker {

    /// Exactly - arity criterion
    ///
    template <unsigned ARITY>
    struct Exactly
    {
        static bool check(unsigned args)
        {
            return ARITY == args;
        }
    };

    /// AtLeast - arity criterion
    ///
    template <unsigned MINARITY>
    struct AtLeast
    {
        static bool check(unsigned args)
        {
            return MINARITY <= args;
        }
    };
    
    /// ArityTraits - arity criterion
    template <typename>
    struct ArityTraits;

#define MICKER_KILL_PARENS(...) \
    , ## __VA_ARGS__

#define MICKER_PREPEND_ITEM(ITEM, PARENTESIZED) \
    (ITEM MICKER_KILL_PARENS PARENTESIZED)

#define MICKER_ARITYTRAITS_SPECIALIZATION(CRITERION, ARITY, PROTO, ADAPT) \
    template <> \
    struct ArityTraits<CRITERION<ARITY> > \
    { \
        template <typename ENV> \
        struct With \
        { \
            typedef ENV& Environment; \
            typedef typename ENV::preferred PrEnvironment; \
            typedef std::string (*Worker)MICKER_PREPEND_ITEM(PrEnvironment, PROTO); \
            template <Worker PROC> \
            static std::string call(Env& env, const std::string& name, const std::deque<std::string>& args) \
            { \
                return PROC MICKER_PREPEND_ITEM(static_cast<Environment>(env).pref(), ADAPT); \
            } \
        }; \
    }; \
    template <> \
    struct ArityTraits<CRITERION<ARITY> >::With<void> \
    { \
      typedef NoEnv<void>& Environment; \
      typedef std::string (*Worker)PROTO; \
      template <Worker PROC> \
      static std::string call(Env&, const std::string& name, const std::deque<std::string>& args) \
      { \
          return PROC ADAPT; \
      } \
    }
  
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 0, (), ());
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 1, (const std::string&), (args.front()));
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 2, (const std::string&, const std::string&), (args.front(), args[1]));
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 3, (const std::string&, const std::string&, const std::string&), (args.front(), args[1], args[2]));
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 4, (const std::string&, const std::string&, const std::string&, const std::string&), (args.front(), args[1], args[2], args[3]));
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 5, (const std::string&, const std::string&, const std::string&, const std::string&, const std::string&), (args.front(), args[1], args[2], args[3], args[4]));
    MICKER_ARITYTRAITS_SPECIALIZATION(Exactly, 6, (const std::string&, const std::string&, const std::string&, const std::string&, const std::string&, const std::string&), (args.front(), args[1], args[2], args[3], args[4], args[5]));

    MICKER_ARITYTRAITS_SPECIALIZATION(AtLeast, 0, (const std::deque<std::string>&), (args));
    MICKER_ARITYTRAITS_SPECIALIZATION(AtLeast, 1, (const std::string&, const std::deque<std::string>&), (args.front(), args));
    MICKER_ARITYTRAITS_SPECIALIZATION(AtLeast, 2, (const std::string&, const std::string&, const std::deque<std::string>&), (args.front(), args[1], args));

#undef MICKER_ARITYTRAITS_SPECIALIZATION
#undef MICKER_KILL_PARENS
#undef MICKER_PREPEND_ITEM

} // namespace

#endif // micker_ARITIES_hpp
